/**
 * rommanager.c
 *
 * @author  Philippe Johnson
 */

#include "common.h"
#include "rommanager.h"

/** VISIBLE FUNCTIONS **/
BOOL loadRom( char* filename, RomInfo* romInfo ) {
    int offset;
    int romSize;
    FILE* romFile = 0;

    assert( filename );
    assert( romInfo  );

    if( romData ) {
        DEBUGMSG("loadRom error: rom data already loaded\n");
        goto loadRomFail;
    }

    romFile = fopen( filename, "rb" );
    if( !romFile ) {
        DEBUGMSG("loadRom error: failed to open file\n");
        goto loadRomFail;
    }
    
    if( !( offset = getRomInfo( romFile, romInfo ) ) ) {
        DEBUGMSG("loadRom error: could not find ROM information\n");
        goto loadRomFail;
    }

    /* Allocate the proper file size */
    switch( romInfo->size ) {
        case ROMSIZE_2MBITS:
            romSize = 262144; /* TODO: magic numbers are stupid */
        break;
        case ROMSIZE_4MBITS:
            romSize = 524288;
        break;
        case ROMSIZE_8MBITS:
            romSize = 1048576;
        break;
        case ROMSIZE_16MBITS:
            romSize = 2097152;
        break;
        case ROMSIZE_32MBITS:
            romSize = 4194304;
        break;
        default:
            DEBUGMSG("loadRom error: Invalid ROM size value\n");
            goto loadRomFail;
    }

    romData = malloc( romSize );
    if( !romData ) {
        DEBUGMSG("loadRom error: Could not allocate ROM space\n");
        goto loadRomFail;
    }

    /* Fill up our romData */
    if( fread( romData, romSize, 1, romFile ) != 1 ) {
        if( feof( romFile ) ) DEBUGMSG("romFile EOF\n");
        else if( ferror( romFile ) ) perror("loadRom");
        goto loadRomFail;
    }

    fclose( romFile );
    return TRUE;

loadRomFail:
    if( romFile ) fclose( romFile );
    free( romData );
    return FALSE;
}

void unloadRom() {
    free( romData );
    romData = 0;
}

int getRomInfo( FILE* romFile, RomInfo* info ) {
    int retVal = 0;
    int initialFilePosition;

    assert(romFile);
    assert(info);

    /* To restore before returning */
    initialFilePosition = ftell( romFile );

    /* Check LoROM without a header */
    fseek( romFile, LOROMOFFSET, SEEK_SET );
    fread( ( char * )info, sizeof( *info ), 1, romFile );

    if( ( info->checksum | info->inverseChecksum ) == 0xFFFF ) {
        DEBUGMSG( "LoROM without header\n" );
        retVal = ftell( romFile ) - sizeof( *info );
        fseek( romFile, 0, SEEK_SET );
        info->type = LOROM;
        goto getRomInfoDone;
    }

    /* Check LoROM with header */
    fseek( romFile, LOROMOFFSET + ROMHEADER_SIZE, SEEK_SET );
    fread( ( char * )info, sizeof( *info ), 1, romFile );

    if( ( info->checksum | info->inverseChecksum ) == 0xFFFF ) {
        DEBUGMSG( "LoROM with header\n" );
        retVal = ftell( romFile ) - sizeof( *info );
        fseek( romFile, ROMHEADER_SIZE, SEEK_SET );
        goto getRomInfoDone;
    }

    /* Check HiROM without a header */
    fseek( romFile, HIROMOFFSET, SEEK_SET );
    fread( ( char * )info, sizeof( *info ), 1, romFile );

    if( ( info->checksum | info->inverseChecksum ) == 0xFFFF ) {
        DEBUGMSG( "HiROM without header\n" );
        retVal = ftell( romFile ) - sizeof( *info );
        fseek( romFile, 0, SEEK_SET );
        goto getRomInfoDone;
    }

    /* Check HiROM with header */
    fseek( romFile, HIROMOFFSET + ROMHEADER_SIZE, SEEK_SET );
    fread( ( char * )info, sizeof( *info ), 1, romFile );

    if( ( info->checksum | info->inverseChecksum ) == 0xFFFF ) {
        DEBUGMSG( "HiROM with header\n" );
        retVal = ftell( romFile ) - sizeof( *info );
        fseek( romFile, ROMHEADER_SIZE, SEEK_SET );
        goto getRomInfoDone;
    }

    fseek( romFile, initialFilePosition, SEEK_SET );

getRomInfoDone:
    return retVal;
}

void* getRomData() {
    return romData;
}

/** MODULE-SPECIFIC FUNCTIONS **/

/**
 * getSWCHeader
 * Fills out an SWCHeader struct of a ROM if an
 * SWC header is present. Restores the file pointer
 * to initial state before returning.
 *
 * @param romFile Pointer to the open ROM file
 * @param header A pointer to the SWCHeader struct
 *
 * @return TRUE if SWC header, FALSE otherwise
 */
BOOL getSWCHeader( FILE *romFile, SWCHeader* header ) {
    /* TODO: stub */
    return TRUE;
}

/**
 * getFIGHeader
 * Fills out an FIGHeader struct of a ROM if the
 * header is present. Restores the file pointer
 * to initial state before returning.
 *
 * @param romFile The name of the ROM file
 * @param header A pointer to the FIGHeader struct
 *
 * @return TRUE if FIG header, FALSE otherwise
 */
BOOL getFIGHeader( FILE* romFile ) {
    /* TODO: stub */
    return TRUE;
}
